Ce billet fait suite à une question posée sur le fédivers : comment profiter d'une zone home.arpa. à soi et d'enregistrements SSHFP pour faciliter la gestion de SSH sur le réseau local. Question non triviale car SSHFP nécessite DNSSEC. Voyons donc comment signer une zone locale. La technique va impliquer 2 outils supplémentaires en plus d'Unbound : NSD pour servir la zone locale et ldns pour gérer DNSSEC (génération de clés, signature de la zone). Pour ce billet, je ne rentrerai pas dans les détails de DNSSEC (ZSK, KSK, DS...), une connaissance préalable de la chose est donc préférable.
Donc, ce n'est plus Unbound qui va servir la zone locale, mais NSD, un vrai serveur faisant autorité. La première étape va donc être de nettoyer la configuration Unbound pour y enlever les paramètres local-zone, local-data,... (si présents) et installer NSD. On édite un fichier de configuration dans /etc/nsd/nsd.conf.d/
server:
interface: 127.0.0.1:53530 # On n'écoute que localement et surtout pas sur le port 53
zone:
name: "home.arpa"
zonefile: "/etc/nsd/nsd.conf.d/home.arpa.zone.signed"
Le fichier de zone n'existe pas encore, fabriquons le. Avant d'avoir un fichier de zone signé, il faut un fichier... sans signatures. Il devrait ressembler à quelque chose comme cela :
$TTL 86400
$ORIGIN home.arpa.
@ 14400 IN SOA ns.home.arpa. hostmaster.home.arpa. (
1 ; Serial
4H ; refresh after 4 hour
1H ; retry after 1 hour
3W ; expire after 3 weeks
1H) ; minimun TTL of 1 hour
;IPv4
router IN A 192.168.0.1
cthulhu IN A 192.168.0.2
eth.azathoth IN A 192.168.0.3
wifi.azathoth IN A 192.168.0.4
;IPv6
router IN AAAA 2001:db8:1a1a:ca11::1
cthulhu IN AAAA 2001:db8:1a1a:ca11:0f:c700:100:1926
;Divers
azathoth IN TXT "Une adresse par interface"
Quelques remarques :
•.Les commentaires sont signalés par un point-virgule
•.Si on n'indique pas de TTL, NSD utilisera celui indiqué par défaut via $TTL
•.Vous pouvez mettre un peu n'importe quoi dans le SOA pour le MNAME (ici ns.home.arpa.) et le RNAME (ici hostmaster.home.arpa.)
•.Bien penser au point final pour home.arpa.. Si l'on indique seulement home.arpa, NSD y ajoutera $ORIGIN (on aura donc home.arpa.home.arpa.)
•.Le numéro de série n'a pas trop d'importance (il n'y aura pas de serveurs secondaires demandant de la donnée fraîche). On peut quand même s'amuser à l'incrémenter à chaque modification de la zone, pour prendre l'habitude de le faire (ou écrire un script qui le fait)
•.Pour l'instant, NSD ne fonctionne pas, on le relancera plus tard
C'est donc ldns qui va gérer la partie DNSSEC. C'est une boîte à outils très utile, notamment pour une gestion artisanale de ses zones signées (pour industrialiser, on utiliserait OpenDNSSEC). Sous Debian, il s'agit du paquet ldnsutils. On l'installe :
# apt-get install ldns-utils
Les 2 outils qui nous intéressent sont ldns-keygen et ldns-signzone. Les noms ont l'avantage d'être explicites.
Il faut générer une ZSK et une KSK. Commençons par la ZSK. Avant cela, il faut choisir un algorithme. ldns est dépendant d'OpenSSL de ce point de vue là. Pour connaître la liste supportée par ldns, on entre :
# ldns-keygen -a list
Possible algorithms:
RSAMD5
RSASHA1
RSASHA1-NSEC3-SHA1
RSASHA256
RSASHA512
ECDSAP256SHA256
ECDSAP384SHA384
ED25519
ED448
DSA
DSA-NSEC3-SHA1
hmac-md5.sig-alg.reg.int
hmac-sha1
hmac-sha256
hmac-sha224
hmac-sha384
hmac-sha512
Pour ce billet, on prendra un algorithme répandu : ECDSAP256SHA256. Une précision importante : ldns n'a pas d'option pour indiquer le répertoire où écrire les clés générées, il le fait automatiquement dans le répertoire courant. Pensez à bien se positionner avant la génération. Cette étape génera jusqu'à 3 fichiers pour chaque clé :
•.K<domaine>+<alg>+<id>.key : la clé publique au format d'un enregistrement DNS (example.com. IN DNSKEY...)
•.K<domaine>+<alg>+<id>.private : la clé privée. On a beau être sur une zone locale, pensez à protéger ce fichier tout de même
•.K<domaine>+<alg>+<id>.ds : l'enregistrement DS que l'on met normalement dans la zone parente, généré uniquement pour la KSK
Générons donc notre ZSK
# ldns-keygen -a ECDSAP256SHA256 home.arpa
Khome.arpa.+013+53551
Puis la KSK. Même commande, on ajoute uniquement le paramètre -k
# ldns-keygen -k -a ECDSAP256SHA256 home.arpa
Khome.arpa.+013+18062
On vérifie que l'on a tout :
# ls Khome.arpa.+013+*
Khome.arpa.+013+18062.ds Khome.arpa.+013+18062.key Khome.arpa.+013+18062.private Khome.arpa.+013+53551.key Khome.arpa.+013+53551.private
Nous avons donc une clé avec l'id 18062
cat Khome.arpa.+013+18062.key
home.arpa. IN DNSKEY 257 3 13 bOcYev/grRO3AOc/mtc+VJNIsfpzTP2da1LhUreOrMUViE1CGY0wye1WXsAAqHIWBD0z5EtZCKV6qS0Y8OOg2g== ;{id = 18062 (ksk), size = 256b}
On a le drapeau 257, c'est bien une KSK. Vérifions la clé avec l'id 53551
# cat Khome.arpa.+013+53551.key
home.arpa. IN DNSKEY 256 3 13 l+GT51VQ/PgJuJUXr3j6RkXqyDSCj1djhK+HOyoviKQnqmRi5Qs/6m71jsQ4lxj9caVXC0KlJB1OMove0keDkw== ;{id = 53551 (zsk), size = 256b}
On a le drapeau 256, c'est bien une ZSK. Les indications en commentaires sont correctes, ldns a bien fait son travail.
Pour la signature, nous allons simplifier la procédure au maximum en ne s'occupant pas de NSEC (et NSEC3), ldns générant par défaut des enregistrement NSEC. Pour la durée de vie des signatures, 2 possibiltés :
•.Mettre une valeur standard, comme on en trouve dans la nature, dans les 30-35 jours mais qui va nécessiter de re-signer la zone régulièrement
•.Mettre une grande valeur, mettons 10 ans, afin d'être tranquille. Nous allons opter pour cette option. À voir si cela ne gène pas dans le temps (ça ne devrait pas, mais sait-on jamais)
On signe donc notre zone :
# ldns-signzone -e $(($(date +%s) + 315365000)) -o home.arpa -f /etc/nsd/nsd.conf.d/home.arpa.zone.signed /etc/nsd/nsd.conf.d/home.arpa.zone Khome.arpa.+013+53551 Khome.arpa.+013+18062
S'il n'y a pas de messages d'erreurs, vérifions le fichier généré :
# cat /etc/nsd/nsd.conf.d/home.arpa.zone.signed
home.arpa. 14400 IN SOA ns.home.arpa. hostmaster.home.arpa. 1 14400 3600 1814400 3600
home.arpa. 14400 IN RRSIG SOA 13 2 14400 20301112033940 20201114021620 53551 home.arpa. u7KPfzsPJXeal/24cdLbkCpCQzcb25Sn+Dch6yjIaKYv5M9/8b6pHR/XWVcvTUNVKL/P01FT1LtcrSEyTABKkw==
home.arpa. 14400 IN DNSKEY 256 3 13 l+GT51VQ/PgJuJUXr3j6RkXqyDSCj1djhK+HOyoviKQnqmRi5Qs/6m71jsQ4lxj9caVXC0KlJB1OMove0keDkw== ;{id = 53551 (zsk), size = 256b}
home.arpa. 14400 IN DNSKEY 257 3 13 bOcYev/grRO3AOc/mtc+VJNIsfpzTP2da1LhUreOrMUViE1CGY0wye1WXsAAqHIWBD0z5EtZCKV6qS0Y8OOg2g== ;{id = 18062 (ksk), size = 256b}
home.arpa. 14400 IN RRSIG DNSKEY 13 2 14400 20301112033940 20201114021620 18062 home.arpa. 85j6NrPruCrPcytBe4ckfse9EnCfUfYeMjHQ69+chYtvk6J0wlmAF1SraSS/wgCWx2FciFhCMdzCYAjUr7kfjQ==
home.arpa. 3600 IN NSEC azathoth.home.arpa. SOA RRSIG NSEC DNSKEY
...
Tout semble bon. On peut relancer NSD.
# systemctl restart nsd
Si dans le futur vous modifiez la zone :
•.Pensez à la resigner !
•.Pas besoin de relancer NSD, on peut recharger la zone via la commande :
# nsd-control reload home.arpa
Le plus dur est fait, reste à configurer le résolveur. Il y a 2 éléments à passer à Unbound :
•.La configuration de la zone, où on lui dit à qui envoyer les requêtes. Cela se fera via une stub-zone
•.Lui donner de quoi valider les signature. On utilisera trust-anchor
Pour ce dernier paramètre, on récupère l'enregistrement DS précédemment généré
# cat Khome.arpa.+013+18062.ds
home.arpa. IN DS 18062 13 2 4eda0e1d8f64c986f3f1e5bb1f2ba834af9847955b0b1a8d5fc29c1e0cb9791a
On le copie, et on configure Unbound
server:
...
trust-anchor: "home.arpa. IN DS 18062 13 2 4eda0e1d8f64c986f3f1e5bb1f2ba834af9847955b0b1a8d5fc29c1e0cb9791a"
stub-zone:
name: "home.arpa."
stub-addr: 127.0.0.1@53530
On le redémarre et on teste
# dig cthulhu.home.arpa
; <<>> DiG 9.16.6-Debian <<>> cthulhu.home.arpa
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 6043
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;test.home.arpa. IN A
;; ANSWER SECTION:
cthulhu.home.arpa. 86400 IN A 192.168.0.2
;; Query time: 0 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: sam. nov. 14 03:31:05 CET 2020
;; MSG SIZE rcvd: 59
On a bien le bit AD (Authentificated Data), indiquant que la réponse est validée. Tout fonctionne bien donc. Une dernière chose : sur les systèmes récents, disposant de la libc6 2.31 (et supérieure), le système ne transmet plus automatiquement le bit AD aux applications qui en ont besoin (c'est a priori le cas de SSH lorsqu'on utilise SSHFP — à tester ceci dit, n'utilisant pas SSHFP — ou gnutls-cli lorsque l'on essaye d'utiliser DANE). Pour que les applications disposent de ce bit, il faut ajouter l'option suivante à votre resolv.conf :
options trust-ad
Ajoutons que ce nouveau comportement par défaut de libc6 est pensé pour les environnement où le résolveur n'est pas forcément fiable. S'il tourne localement, ce n'est normalement pas le cas (à moins de ne pas se faire confiance ☺).